package com.yanqu.road.server.channelserver.manager.activity.washe.game;

import com.yanqu.road.entity.activity.washe.config.WasheCardConfig;
import com.yanqu.road.entity.activity.washe.config.WasheConfig;
import com.yanqu.road.entity.activity.washe.config.WasheHardLevelConfig;
import com.yanqu.road.entity.activity.washe.config.WasheModeConfig;
import com.yanqu.road.entity.activity.washe.eliminate.WasheAiMoveParam;
import com.yanqu.road.entity.activity.washe.eliminate.WasheBlock;
import com.yanqu.road.entity.activity.washe.eliminate.WasheBlockEffect;
import com.yanqu.road.entity.activity.washe.eliminate.WasheBoom;
import com.yanqu.road.entity.activity.washe.eliminate.WasheEliminateTip;
import com.yanqu.road.entity.activity.washe.eliminate.WasheGameData;
import com.yanqu.road.entity.activity.washe.eliminate.WasheMoveParam;
import com.yanqu.road.entity.activity.washe.eliminate.WasheOperateEffect;
import com.yanqu.road.entity.activity.washe.eliminate.WasheOperateResult;
import com.yanqu.road.entity.activity.washe.eliminate.WasheOperateRoundResult;
import com.yanqu.road.entity.activity.washe.eliminate.WasheOperateShow;
import com.yanqu.road.entity.activity.washe.eliminate.WasheOperateStageResult;
import com.yanqu.road.entity.activity.washe.eliminate.WasheUseSkillParam;
import com.yanqu.road.entity.activity.washe.game.WasheRoomData;
import com.yanqu.road.entity.activity.washe.game.WasheUserGameCard;
import com.yanqu.road.entity.activity.washe.game.WasheUserGameData;
import com.yanqu.road.entity.activity.washe.game.WasheUserGameSkill;
import com.yanqu.road.entity.enums.washe.eWasheBlockEffectType;
import com.yanqu.road.entity.enums.washe.eWasheBlockType;
import com.yanqu.road.entity.enums.washe.eWasheBoomType;
import com.yanqu.road.entity.enums.washe.eWasheColorType;
import com.yanqu.road.entity.enums.washe.eWasheDirectionType;
import com.yanqu.road.entity.enums.washe.eWasheEffectType;
import com.yanqu.road.entity.enums.washe.eWasheOperateType;
import com.yanqu.road.entity.enums.washe.eWasheSceneType;
import com.yanqu.road.entity.enums.washe.eWasheSkillType;
import com.yanqu.road.server.protocol.GameErrorCode;
import com.yanqu.road.utils.RandomHelper;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.stream.Collectors;

public class WasheGameLogic {

    public static WasheOperateResult operateMove(WasheConfig config, WasheRoomData roomData, boolean isPlayer1, WasheMoveParam moveParam, int guideNo, int roomRound) {
        WasheUserGameData userGameData = isPlayer1 ? roomData.getPlayer1() : roomData.getPlayer2();
        WasheGameData cloneGameData = roomData.getGameData().clone();
        WasheOperateResult result = new WasheOperateResult(eWasheOperateType.Move, isStepMax(config, roomData.getModeId(), userGameData), roomData.getTurnPlayer());
        // 交换方块
        result.setRet(WasheGameLogic.checkMoveBlock(cloneGameData, moveParam, result));
        if (result.getRet() != 0) {
            return result;
        }
        Random random = new Random();
        moveBlock(config, random, cloneGameData, result, moveParam, userGameData, guideNo, roomRound, roomData.getTurnPlayer());
        if (result.getRet() != 0) {
            return result;
        }
        WasheSceneLogic.operateEnd(config, random, cloneGameData, result);
        // 结果
        result.setFinalData(cloneGameData);
        return result;
    }

    public static WasheOperateResult operateUseSkill(WasheConfig config, WasheRoomData roomData, boolean isPlayer1, WasheUseSkillParam skillParam, int guideDropNo, int roomRound) {
        WasheUserGameData userGameData = isPlayer1 ? roomData.getPlayer1() : roomData.getPlayer2();
        WasheGameData cloneGameData = roomData.getGameData().clone();
        // 判断本次操作允不允许加步数， （步数满了且不是快速充能技能操作 不允许加步数）
        boolean notAddStep = isStepMax(config, roomData.getModeId(), userGameData) && skillParam.getSkillId() != eWasheSkillType.Skill_1003.getValue();
        WasheOperateResult result = new WasheOperateResult(eWasheOperateType.UseSkill, notAddStep, roomData.getTurnPlayer());
        result.setSkillId(skillParam.getSkillId());
        // 使用技能
        result.setRet(checkUseSkill(userGameData, skillParam, cloneGameData, result));
        if (result.getRet() != 0) {
            return result;
        }
        // 补充点参数
        skillParam.setSide(isPlayer1 ? 1 : 2);
        Random random = new Random();
        useSkill(config, cloneGameData, skillParam, random, result, userGameData, guideDropNo, roomRound, roomData.getTurnPlayer());
        if (result.getRet() != 0) {
            return result;
        }
        WasheSceneLogic.operateEnd(config, random, cloneGameData, result);
        // 结果
        result.setFinalData(cloneGameData);
        return result;
    }

    public static WasheOperateResult operateUseCard(WasheConfig config, WasheRoomData roomData, boolean isPlayer1, WasheUseSkillParam skillParam, int guideDropNo, int roomRound) {
        WasheUserGameData userGameData = isPlayer1 ? roomData.getPlayer1() : roomData.getPlayer2();
        WasheGameData cloneGameData = roomData.getGameData().clone();
        WasheOperateResult result = new WasheOperateResult(eWasheOperateType.UseCard, isStepMax(config, roomData.getModeId(), userGameData), roomData.getTurnPlayer());
        result.setSkillId(skillParam.getSkillId());
        // 使用卡片
        result.setRet(checkUseCard(cloneGameData, userGameData, skillParam));
        if (result.getRet() != 0) {
            return result;
        }
        Random random = new Random();
        useCard(config, random, cloneGameData, result, skillParam, userGameData, guideDropNo, roomRound, roomData.getTurnPlayer());
        if (result.getRet() != 0) {
            return result;
        }
        WasheSceneLogic.operateEnd(config, random, cloneGameData, result);
        // 结果
        result.setFinalData(cloneGameData);
        return result;
    }

    // ============================= 生成盘面 ==============================

    public static List<List<WasheBlock>> getInitBlockList(Random random, WasheConfig config) {
        int width = config.getBlockListWidth();
        int height = config.getBlockListHeight();
        int colorCount = config.getColorNum();
        int limitCount = height * width / colorCount;

        int tryTimes = 30; // 大于10
        int tryBestTimes = 15; // 比tryTimes小，大于等于0

        for (int i = 0; i < tryTimes; i++) {

            // 各颜色数量
            Map<Integer, Integer> colorCountMap = new HashMap<>();

            List<List<WasheBlock>> listList = new ArrayList<>();
            for (int y = 0; y < height; y++) {
                listList.add(new ArrayList<>());
                for (int x = 0; x < width; x++) {
                    List<Integer> colorList = getCanCreateColor(listList, colorCount, x, y);

                    int chooseColor;
                    // 所有颜色都不能选，这里说明代码或配置有问题
                    if (colorList.isEmpty()) {
                        chooseColor = eWasheColorType.Golden.getValue();
                    } else {
                        // 颜色使用数少的权重高
                        List<Integer> weightList = new ArrayList<>();
                        int avgCount = colorList.stream().mapToInt(item -> colorCountMap.getOrDefault(item, 0)).sum() / colorList.size();
                        for (int color : colorList) {
                            int tmpCount = colorCountMap.getOrDefault(color, 0);
                            weightList.add(tmpCount > avgCount || tmpCount >= limitCount ? 250 : 1000);
                        }
                        // 如果可选颜色都没权重了，则全部相同权重随机
                        if (weightList.stream().noneMatch(item -> item > 0)) {
                            for (int index = 0; index < weightList.size(); index++) {
                                weightList.set(index, 1);
                            }
                        }
                        chooseColor = colorList.get(RandomHelper.getRandomIndexByWeight(weightList, random));
                    }

                    listList.get(y).add(new WasheBlock(x, y, chooseColor));
                    colorCountMap.put(chooseColor, colorCountMap.getOrDefault(chooseColor, 0) + 1);
                }
            }

            WasheGameData tmpGameData = WasheGameData.createWasheGameData(eWasheSceneType.Empty.getType(), listList);
            WasheHardLevelConfig checkInitBoardConfig = config.getCheckInitBoardConfig();
            WasheAiMoveParam bestAiMove = WasheAiLogic.getBestAiMove(tmpGameData, 1, null, checkInitBoardConfig);
            if (bestAiMove != null) {
                int moveWeight = bestAiMove.getWeight(checkInitBoardConfig);
                if (i < tryBestTimes && moveWeight < checkInitBoardConfig.getMoveWeightList().get(0)) {
                    return listList;
                }
                if (i >= tryBestTimes) {
                    return listList;
                }
            }

        }

        // 说明代码有问题
        throw new RuntimeException("WasheGameLogic getInitBlockList error!");
    }

    private static List<Integer> getCanCreateColor(List<List<WasheBlock>> blockList, int colorCount, int x, int y) {
        Set<Integer> removeSet = new HashSet<>();
        // 下面2个颜色相同
        if (y - 2 >= 0
                && blockList.get(y - 1).get(x).getColor() == blockList.get(y - 2).get(x).getColor()) {
            removeSet.add(blockList.get(y - 1).get(x).getColor());
        }
        // 左边2个颜色相同
        if (x - 2 >= 0
                && blockList.get(y).get(x - 1).getColor() == blockList.get(y).get(x - 2).getColor()) {
            removeSet.add(blockList.get(y).get(x - 1).getColor());
        }
        // 结果
        List<Integer> list = new ArrayList<>();
        for (int i = 1; i <= colorCount; i++) {
            if (!removeSet.contains(i)) {
                list.add(i);
            }
        }
        return list;
    }

    // ============================= 主要操作 ==============================

    public static int checkMoveBlock(WasheGameData gameData, WasheMoveParam moveParam, WasheOperateResult result) {
        // 检查能否移动
        int ret = checkExchangeBlock(gameData.getBlockList(), moveParam);
        if (ret != 0) {
            return ret;
        }
        // 移动后能否消除
        ret = checkEliminate(gameData, moveParam, null, result);
        if (ret != 0) {
            return ret;
        }
        return 0;
    }

    private static int checkExchangeBlock(List<List<WasheBlock>> blockList, WasheMoveParam moveParam) {
        // 能否移动
        if (!isXyExist(blockList, moveParam.getFromX(), moveParam.getFromY())
                || !isXyExist(blockList, moveParam.getToX(), moveParam.getToY())) {
            return GameErrorCode.E_WASHE_POS_NO_EXIST;
        }
        int subX = Math.abs(moveParam.getFromX() - moveParam.getToX());
        int subY = Math.abs(moveParam.getFromY() - moveParam.getToY());
        if (subX + subY != 1) {
            return GameErrorCode.E_WASHE_POS_CAN_NOT_MOVE;
        }
        return 0;
    }

    public static void moveBlock(WasheConfig config, Random random, WasheGameData gameData, WasheOperateResult result, WasheMoveParam moveParam, WasheUserGameData userGameData, int guideDropNo, int roomRound, int turnPlayer) {
        // 交换方块
        exchangeBlock(gameData.getBlockList(), moveParam.getFromX(), moveParam.getFromY(), moveParam.getToX(), moveParam.getToY(), result);
        // 消除
        bigEliminate(config, gameData, userGameData, moveParam, new HashSet<>(), result, random, guideDropNo, roomRound, turnPlayer);
    }

    private static int checkUseCard(WasheGameData gameData, WasheUserGameData userGameData, WasheUseSkillParam skillParam) {
        WasheUserGameCard card = userGameData.getCard();
        if (card.getCardId() != skillParam.getSkillId()) {
            return GameErrorCode.E_WASHE_CARD_NO_EXIST;
        }
        if (card.getCardMp() < card.getCardMaxMp()) {
            return GameErrorCode.E_WASHE_CARD_MP_NO_FULL;
        }

        // 判断方块存不存在（不需要方块的都是0,0，也是存在的）
        if (!isXyExist(gameData.getBlockList(), skillParam.getX(), skillParam.getY())
                || !isXyExist(gameData.getBlockList(), skillParam.getToY(), skillParam.getToX())) {
            return GameErrorCode.E_WASHE_POS_NO_EXIST;
        }

        // 特殊技能判断
        if (skillParam.getSkillId() == eWasheSkillType.Card_09.getValue()) {
            return checkSkill_09_beforeUse(gameData, skillParam);
        } else if (skillParam.getSkillId() == eWasheSkillType.Card_11.getValue()) {
            return checkSkill_11_beforeUse(gameData, skillParam);
        } else if (skillParam.getSkillId()== eWasheSkillType.Card_24.getValue()){
            return checkSkill_24_beforeUse(gameData, skillParam);
        }else {
            return 0;
        }
    }

    private static void useCard(WasheConfig config, Random random, WasheGameData gameData, WasheOperateResult result, WasheUseSkillParam skillParam, WasheUserGameData userGameData, int guideDropNo, int roomRound, int turnPlayer) {
        // 使用卡片
        Set<WasheBlock> skillEliminateBlock = WasheSkillLogic.handleUseSkill(config, random, gameData, result, skillParam, userGameData, guideDropNo > 0, roomRound, turnPlayer);
        userGameData.getCard().addTurnUseTimes();
        // 消除
        WasheMoveParam moveParam = null;
        if (!(skillParam.getX() == 0 && skillParam.getY() == 0 && skillParam.getToX() == 0 && skillParam.getToY() == 0)) {
            moveParam = new WasheMoveParam(skillParam.getX(), skillParam.getY(), skillParam.getToX(), skillParam.getToY());
        }
        bigEliminate(config, gameData, userGameData, moveParam, skillEliminateBlock, result, random, guideDropNo, roomRound, turnPlayer);
        // 卡片结束效果
        result.addEffect(new WasheOperateEffect(eWasheEffectType.SkillEnd));
    }

    private static int checkUseSkill(WasheUserGameData userGameData, WasheUseSkillParam skillParam, WasheGameData gameData, WasheOperateResult result) {
        // 找技能
        WasheUserGameSkill skill = null;
        for (WasheUserGameSkill tmpSkill : userGameData.getSkillList()) {
            if (tmpSkill.getSkillId() == skillParam.getSkillId()) {
                skill = tmpSkill;
                break;
            }
        }
        if (skill == null) {
            return GameErrorCode.E_WASHE_SKILL_NO_EXIST;
        }
        // 剩余次数
        if (skill.getTimes() <= 0) {
            return GameErrorCode.E_WASHE_SKILL_MAX_TIMES;
        }
        // 特殊技能判断
        if (skill.getSkillId() == eWasheSkillType.Skill_1002.getValue()) {
            return checkSkill_1002_beforeUse(gameData, skillParam);
        } else if (skill.getSkillId() == eWasheSkillType.Skill_1003.getValue()) {
            return checkSkill_1003_beforeUse(gameData, skillParam, result);
        } else if (skill.getSkillId() == eWasheSkillType.Skill_1011.getValue()) {
            return checkSkill_1011_beforeUse(gameData, skillParam);
        }
        return 0;
    }

    private static void useSkill(WasheConfig config, WasheGameData gameData, WasheUseSkillParam skillParam, Random random, WasheOperateResult result, WasheUserGameData userGameData, int guideDropNo, int roomRound, int turnPlayer) {
        // 使用技能
        Set<WasheBlock> skillEliminateBlock = WasheSkillLogic.handleUseSkill(config, random, gameData, result, skillParam, userGameData, false, roomRound, turnPlayer);
        // 消除
        bigEliminate(config, gameData, userGameData, null, skillEliminateBlock, result, random, guideDropNo, roomRound, turnPlayer);
        // 技能结束效果
        result.addEffect(new WasheOperateEffect(eWasheEffectType.SkillEnd));
    }

    // ============================= public ==============================

    /**
     * 交换2个方块位置
     *
     * @param result null
     */
    public static void exchangeBlock(List<List<WasheBlock>> blockList, int fromX, int fromY, int toX, int toY,
                                     WasheOperateResult result) {
        WasheBlock from = blockList.get(fromY).get(fromX);
        WasheBlock to = blockList.get(toY).get(toX);
        from.setX(toX);
        from.setY(toY);
        to.setX(fromX);
        to.setY(fromY);
        blockList.get(fromY).set(fromX, to);
        blockList.get(toY).set(toX, from);

        if (result != null) {

            boolean isMerge = isCombBombPart(from) && isCombBombPart(to);

            WasheOperateEffect operateEffect = new WasheOperateEffect(isMerge ? eWasheEffectType.MergeBlock : eWasheEffectType.ExchangeBlock);
            operateEffect.addBlock(to);
            operateEffect.addBlock(from);
            result.addEffect(operateEffect);
        }
    }

    public static boolean canSameColorEliminate(List<List<WasheBlock>> listList, WasheBlock block) {

        if (block.getColor() == eWasheColorType.None.getValue()) {
            return false;
        }

        int x = block.getX();
        int y = block.getY();
        int color = block.getColor();
        int num = 0; // 连续同色的数量

        // 1. 左到右
        for (int tmpX = x - 2; tmpX <= x + 2; tmpX++) {
            if (isXyExist(listList, tmpX, y) && color == listList.get(y).get(tmpX).getColor()) {
                num++;
            } else {
                num = 0;
            }
            if (num >= 3) {
                return true;
            }
        }
        // 2. 下到上
        for (int tmpY = y - 2; tmpY <= y + 2; tmpY++) {
            if (isXyExist(listList, x, tmpY) && color == listList.get(tmpY).get(x).getColor()) {
                num++;
            } else {
                num = 0;
            }
            if (num >= 3) {
                return true;
            }
        }
        return false;
    }

    public static WasheEliminateTip getEliminateTip(WasheGameData gameData) {

        List<List<WasheBlock>> blockList = gameData.getBlockList();

        // 检查组合炸弹
        for (int y = 0; y < blockList.size(); y++) {
            for (int x = 0; x < blockList.get(y).size(); x++) {
                WasheBlock from = blockList.get(y).get(x);
                if (isCombBombPart(from)) {
                    if (isXyExist(blockList, x + 1, y) && isCombBombPart(blockList.get(y).get(x + 1))) {
                        return new WasheEliminateTip(from, blockList.get(y).get(x + 1));
                    }
                    if (isXyExist(blockList, x, y + 1) && isCombBombPart(blockList.get(y + 1).get(x))) {
                        return new WasheEliminateTip(from, blockList.get(y + 1).get(x));
                    }
                }
            }
        }

        // 复制一份出来
        blockList = gameData.clone().getBlockList();

        // 遍历每个方块尝试交换
        for (int y = 0; y < blockList.size(); y++) {
            for (int x = 0; x < blockList.get(y).size(); x++) {

                // 跟右边交换
                int x2 = x + 1;
                int y2 = y;
                if (isXyExist(blockList, x2, y2)) {
                    exchangeBlock(blockList, x, y, x2, y2, null);
                    if (canSameColorEliminate(blockList, blockList.get(y).get(x)) || canSameColorEliminate(blockList, blockList.get(y2).get(x2))) {
                        exchangeBlock(blockList, x, y, x2, y2, null);
                        return new WasheEliminateTip(blockList.get(y).get(x), blockList.get(y2).get(x2));
                    }
                    exchangeBlock(blockList, x, y, x2, y2, null);
                }

                // 跟上边交换
                x2 = x;
                y2 = y + 1;
                if (isXyExist(blockList, x2, y2)) {
                    exchangeBlock(blockList, x, y, x2, y2, null);
                    if (canSameColorEliminate(blockList, blockList.get(y).get(x)) || canSameColorEliminate(blockList, blockList.get(y2).get(x2))) {
                        exchangeBlock(blockList, x, y, x2, y2, null);
                        return new WasheEliminateTip(blockList.get(y).get(x), blockList.get(y2).get(x2));
                    }
                    exchangeBlock(blockList, x, y, x2, y2, null);
                }

            }
        }
        return null;
    }

    public static void rearrangeBlock(WasheGameData gameData, Random random) {

        // 备份下盘面
        List<List<WasheBlock>> backupBoard = gameData.clone().getBlockList();

        // 重排10个结果，取最差的结果，或者无法消除直接取那个结果
        int times = 10;
        List<List<WasheBlock>> worstResult = null;
        int eliminateNum = 0;

        // 多次重排
        for (int i = 0; i < 100; i++) {

            // 恢复备份盘面
            gameData.setBlockList(backupBoard);
            List<List<WasheBlock>> blockList = gameData.getBlockList();

            // 打乱格子
            List<WasheBlock> allList = new ArrayList<>();
            for (List<WasheBlock> list : blockList) {
                for (int j = 0; j < list.size(); j++) {
                    WasheBlock block = list.get(j);
                    if (block.getBlockType() == eWasheBlockType.BaiBaoXiang || block.getBlockType() == eWasheBlockType.SmartBox) {
                        continue; // 跳过百宝箱、机巧盒
                    }
                    allList.add(block);
                }
            }
            Collections.shuffle(allList, random);

            // 重排
            int index = 0;
            for (int y = 0; y < blockList.size(); y++) {
                for (int x = 0; x < blockList.get(y).size(); x++) {
                    WasheBlock oldBlock = blockList.get(y).get(x);
                    if (oldBlock.getBlockType() == eWasheBlockType.BaiBaoXiang || oldBlock.getBlockType() == eWasheBlockType.SmartBox) {
                        continue; // 跳过百宝箱
                    }
                    WasheBlock block = allList.get(index++).clone();
                    block.setX(x);
                    block.setY(y);
                    blockList.get(y).set(x, block);
                }
            }

            // 重排后还是无法消除
            if (getEliminateTip(gameData) == null) {
                continue;
            }

            // 比较直接消除数量
            Set<WasheBlock> tmpSet = new HashSet<>();
            handleSameColorEliminate(blockList, tmpSet);
            if (worstResult == null || tmpSet.size() < eliminateNum) {
                worstResult = gameData.getBlockList();
                eliminateNum = tmpSet.size();
            }

            // 次数判断
            if (--times <= 0 || eliminateNum == 0) {
                break;
            }
        }

        // 替换原来格子
        gameData.setBlockList(worstResult);
    }

    public static boolean isXyExist(List<List<WasheBlock>> blockList, int x, int y) {
        if (y < 0 || y >= blockList.size()) {
            return false;
        }
        if (x < 0 || x >= blockList.get(y).size()) {
            return false;
        }
        if (blockList.get(y).get(x) == null) {
            return false;
        }
        return true;
    }

    public static void turnChangeResetBlock(List<List<WasheBlock>> blockList, WasheOperateResult result) {
        Set<WasheBlock> set = new HashSet<>();
        for (int y = 0; y < blockList.size(); y++) {
            for (int x = 0; x < blockList.get(y).size(); x++) {
                WasheBlock block = blockList.get(y).get(x);

                List<WasheBlockEffect> effectList = block.getEffectList();
                for (int i = 0; i < effectList.size(); i++) {
                    if (effectList.get(i).getEffectType().isTurnReset()) {
                        effectList.remove(i--);
                        set.add(block);
                    }
                }
                if (block.getBlockType() == eWasheBlockType.Rocket) {
                    block.setBlockType(eWasheBlockType.Normal);
                    block.setDirection(eWasheDirectionType.Up);
                    blockList.get(y).set(x, block);
                    set.add(block);
                }
                if (block.getBlockType() == eWasheBlockType.PaperFlower) {
                    block.setBlockType(eWasheBlockType.Normal);
                    blockList.get(y).set(x, block);
                    set.add(block);
                }
                if (block.getBlockType() == eWasheBlockType.LiuXing_SkillCreate) {
                    block.setBlockType(eWasheBlockType.Normal);
                    blockList.get(y).set(x, block);
                    set.add(block);
                }
                if (block.getBlockType() == eWasheBlockType.SmartBox) {
                    // 手工少女-机巧盒 回合结束后替换
                    block.setBlockType(eWasheBlockType.Normal);
                    blockList.get(y).set(x, block);
                    // 随机稳定盘面的颜色
                    Set<Integer> colorSet = new HashSet<>();
                    // 获取上下左右的颜色
                    int[][] dirs = new int[][]{{0, 1}, {0, -1}, {-1, 0}, {1, 0}};
                    for (int[] dir : dirs) {
                        if (WasheGameLogic.isXyExist(blockList, x + dir[0], y + dir[1])) {
                            WasheBlock targetBlock = blockList.get(y + dir[1]).get(x + dir[0]);
                            colorSet.add(targetBlock.getColor());
                        }
                    }
                    List<Integer> chooseColorList = new ArrayList<>();
                    for (eWasheColorType colorType : eWasheColorType.values()) {
                        if (colorType == eWasheColorType.None) {
                            continue;
                        }
                        if (colorSet.contains(colorType.getValue())) {
                            continue;
                        }
                        chooseColorList.add(colorType.getValue());
                    }
                    Random random = new Random();
                    Collections.shuffle(chooseColorList, random);
                    block.setColor(chooseColorList.get(0));
                    set.add(block);
                }
                if (block.getBlockType() == eWasheBlockType.Landmine2) {
                    block.setBlockType(eWasheBlockType.Normal);
                    blockList.get(y).set(x, block);
                    set.add(block);
                }
            }
        }

        // 有变化要添加表现
        if (set.size() > 0) {
            WasheOperateEffect effect = new WasheOperateEffect(eWasheEffectType.UpdateBlock);
            effect.setBlockList(new ArrayList<>(set));

            result.addEffect(effect);
        }
    }

    public static void removeNoBaseFourBigType(Set<WasheBlock> set) {
        List<WasheBlock> removeList = new ArrayList<>();
        for (WasheBlock block : set) {
            if (!isBaseFourBigType(block)) {
                removeList.add(block);
            }
        }
        set.removeAll(removeList);
    }

    public static boolean isBaseFourBigType(WasheBlock block) {
        return block.getBlockType().getBigType() == eWasheBlockType.BIG_TYPE_NORMAL
                || block.getBlockType().getBigType() == eWasheBlockType.BIG_TYPE_NORMAL_BOMB
                || block.getBlockType().getBigType() == eWasheBlockType.BIG_TYPE_SPECIAL
                || block.getBlockType().getBigType() == eWasheBlockType.BIG_TYPE_SPECIAL_BOMB;
    }

    // ============================= private ==============================

    /**
     * 消除边框
     */
    private static void eliminateFrame(WasheGameData gameData, Set<WasheBlock> eliminateSet, eWasheOperateType operateType) {

        // 卡片不能消除框
        if (operateType != eWasheOperateType.Move && operateType != eWasheOperateType.UseSkill) {
            return;
        }

        Map<Integer, Map<Integer, WasheBlock>> frameMap = gameData.getFrameMap();

        List<WasheBlock> list = new ArrayList<>();
        for (WasheBlock block : eliminateSet) {
            if (frameMap.containsKey(block.getY()) && frameMap.get(block.getY()).containsKey(block.getX())) {
                WasheBlock frame = frameMap.get(block.getY()).get(block.getX());

                // 能否消除
                if (frame.getBlockType() == eWasheBlockType.LuoGuXuanTian_Del
                        || frame.getBlockType() == eWasheBlockType.YiBenWanLi
                        || frame.getBlockType() == eWasheBlockType.DianShanLeiMing) {
                    continue;
                }

                // 【正常逻辑】
                list.add(frame);
                frameMap.get(frame.getY()).remove(frame.getX());

                // 消除后处理
                if (frame.getBlockType() == eWasheBlockType.LuoGuXuanTian) {

                    WasheBlock clone = frame.clone();
                    clone.setBlockType(eWasheBlockType.LuoGuXuanTian_Del);
                    frameMap.get(frame.getY()).put(frame.getX(), clone);

                    WasheBlockEffect effect = new WasheBlockEffect(eWasheBlockEffectType.AddScore_Hide);
                    for (WasheBlockEffect tmpEffect : frame.getEffectList()) {
                        if (tmpEffect.getEffectType() == eWasheBlockEffectType.LuoGuXuanTian) {
                            effect.setEffectValue(effect.getEffectValue() + tmpEffect.getEffectValue());
                        }
                    }
                    block.addEffect(effect);
                }
            }
        }

        eliminateSet.addAll(list);
    }

    public static void bigEliminate(WasheConfig config, WasheGameData gameData, WasheUserGameData userGameData,
                                    WasheMoveParam moveParam, Set<WasheBlock> firstBlock, WasheOperateResult result,
                                    Random random, int guideDropNo, int roomRound, int turnPlayer) {

        // 正常消除一次
        eliminate(config, gameData, userGameData, moveParam, firstBlock, result, random, guideDropNo, roomRound, turnPlayer);

        // 消除结束操作
        WasheSceneLogic.eliminateEnd(config, random, gameData, result, userGameData, roomRound, turnPlayer);

        // 盘面稳定后技能
        int cardId = userGameData.getCard().getCardId();
        if (cardId == eWasheSkillType.Card_13.getValue()) {
            handleSkill_13_moveEnd(config, gameData, userGameData, result, result.getType(), random);
        } else if (cardId == eWasheSkillType.Card_22.getValue()) {
            handleSkill_22_eliminateEnd(gameData, userGameData, result, result.getType(), random);
        } else if (cardId == eWasheSkillType.Card_23.getValue()) {
            handleSkill_23_moveEnd(config, gameData, userGameData, result, result.getType(), random);
        }
    }


    /**
     * 消除
     */
    public static void eliminate(WasheConfig config, WasheGameData gameData, WasheUserGameData userGameData,
                                 WasheMoveParam moveParam, Set<WasheBlock> firstBlock, WasheOperateResult result,
                                 Random random, int guideDropNo, int roomRound, int turnPlayer) {

        removeNoBaseFourBigType(firstBlock);

        eWasheOperateType operateType = result.getType();

        Set<WasheBlock> lastRoundDropSet = new HashSet<>();
        for (int round = 1; round <= 100; round++) {

            WasheSceneLogic.scene_08_fixColor(gameData, result);

            List<List<WasheBlock>> blockList = gameData.getBlockList();

            // 本轮参数
            Set<WasheBlock> roundEliminateSet = new HashSet<>();
            List<WasheBoom> roundBoomList = new ArrayList<>();
            Set<WasheBlock> nextStageBoomSet = new HashSet<>();

            WasheOperateRoundResult roundResult = new WasheOperateRoundResult();
            for (int stage = 1; stage <= 100; stage++) {

                // 本阶段参数
                Set<WasheBlock> backupEliminateSet = new HashSet<>(roundEliminateSet);
                Set<WasheBoom> backupBoomList = new HashSet<>(roundBoomList);
                Set<WasheBlock> backupNextStageBoomSet = new HashSet<>(nextStageBoomSet);
                WasheOperateStageResult stageResult = new WasheOperateStageResult();

                // 上一阶段炸弹
                if (round > 1 || stage > 1) {
                    handleBombBoom(config, blockList, nextStageBoomSet, roundEliminateSet, roundBoomList);
                }

                // 组合炸弹
                if (round == 1 && stage == 1 && operateType == eWasheOperateType.Move) {
                    handleCombBombBoom(config, blockList, moveParam, roundEliminateSet, roundBoomList);
                }

                // 同色消除 + 快速爆炸
                if (stage == 1) {
                    handleSameColorEliminate(blockList, roundEliminateSet);
                    WasheSceneLogic.afterSameColorEliminate(config, gameData, roundEliminateSet, stageResult, operateType);
                    fastBoom(config, gameData, roundEliminateSet, roundBoomList, operateType);
                }

                // 触发技能消除
                if (round == 1 && stage == 1) {
                    handleSkill_13_move(config, gameData, roundEliminateSet, roundBoomList, userGameData, operateType); // 这个技能比较特殊，移动触发消除才能触发
                    roundEliminateSet.addAll(firstBlock);
                    fastBoom(config, gameData, roundEliminateSet, roundBoomList, operateType);
                }

                // 检查下边框消除
                eliminateFrame(gameData, roundEliminateSet, operateType);

                // 添加下阶段爆炸炸弹
                nextStageBoomSet = getCanBoomBlock(roundBoomList, roundEliminateSet);

                // 记录阶段结果
                stageResult.getBoomList().addAll(roundBoomList.stream().skip(backupBoomList.size()).collect(Collectors.toList()));
                stageResult.getEliminateList().addAll(roundEliminateSet.stream().filter(item -> !backupEliminateSet.contains(item)).collect(Collectors.toList()));
                stageResult.getEliminateList().addAll(backupNextStageBoomSet);
                stageResult.getEliminateList().removeAll(nextStageBoomSet);
                roundResult.addStageResult(stageResult);
                if (nextStageBoomSet.isEmpty()) {
                    break;
                }
            }
            // 本轮没有可以消除的
            if (!roundEliminateSet.isEmpty()) {

                // 1.消除的格子置空
                setEliminateEmpty(blockList, roundEliminateSet);

                // 2.生成炸弹
                createBomb(config, moveParam, result, random, gameData, lastRoundDropSet, round, roundEliminateSet, roundResult);

                // 3.掉落方块
                lastRoundDropSet = dropBlock(config, blockList, random, guideDropNo, gameData, roomRound, turnPlayer);

                // 4.设置结果
                roundResult.setGameData(gameData.clone());
                result.addRoundResult(roundResult);

                // 5.检查重排
                checkRearrange(result, gameData, random);

            } else {
                break;
            }

        }

    }

    /**
     * 检查能否消除
     *
     * @param moveParam  null
     * @param firstBlock null
     */
    private static int checkEliminate(WasheGameData gameData, WasheMoveParam moveParam, Set<WasheBlock> firstBlock, WasheOperateResult result) {

        if (firstBlock != null) {
            // 检查技能提前消除方块
            if (firstBlock.size() > 0) {
                return 0;
            }
        }

        List<List<WasheBlock>> blockList = gameData.getBlockList();

        if (moveParam != null) {
            // 检查移动组合炸弹
            WasheBlock from = blockList.get(moveParam.getFromY()).get(moveParam.getFromX());
            WasheBlock to = blockList.get(moveParam.getToY()).get(moveParam.getToX());
            if (isCombBombPart(from) && isCombBombPart(to)) {
                return 0;
            }
            // 检查同色方块消除
            exchangeBlock(blockList, moveParam.getFromX(), moveParam.getFromY(), moveParam.getToX(), moveParam.getToY(), null);
            WasheSceneLogic.scene_08_fixColor(gameData, result);
            for (List<WasheBlock> list : blockList) {
                for (WasheBlock block : list) {
                    if (canSameColorEliminate(blockList, block)) {
                        exchangeBlock(blockList, moveParam.getFromX(), moveParam.getFromY(), moveParam.getToX(), moveParam.getToY(), null);
                        return 0;
                    }
                }
            }
            exchangeBlock(blockList, moveParam.getFromX(), moveParam.getFromY(), moveParam.getToX(), moveParam.getToY(), null);
        }

        return GameErrorCode.E_WASHE_CAN_NOT_ELIMINATE;
    }

    private static boolean isStepMax(WasheConfig config, int modeId, WasheUserGameData userGameData) {
        WasheModeConfig modeConfig = config.getModeConfigMap().get(modeId);
        return userGameData.getStep() >= modeConfig.getMaxStep();
    }

    private static void fastBoom(WasheConfig config, WasheGameData gameData, Set<WasheBlock> roundEliminateSet, List<WasheBoom> roundBoomList, eWasheOperateType operateType) {
        // 检查下边框消除
        eliminateFrame(gameData, roundEliminateSet, operateType);

        // 找未爆炸的炸弹
        Set<WasheBlock> set = getCanBoomBlock(roundBoomList, roundEliminateSet);
        // 马上爆炸
        handleBombBoom(config, gameData.getBlockList(), set, roundEliminateSet, roundBoomList);
    }

    private static Set<WasheBlock> getCanBoomBlock(List<WasheBoom> roundBoomList, Set<WasheBlock> handleBlockSet) {
        Set<WasheBlock> canBoomBlockSet = new HashSet<>();
        for (WasheBlock block : handleBlockSet) {
            List<eWasheBoomType> boomTypeList = getBoomType(block);
            if (boomTypeList.size() > 0 && roundBoomList.stream().noneMatch(item -> item.getX() == block.getX() && item.getY() == block.getY() && ((item.getBoomType().getBigType() == eWasheBoomType.BIG_TYPE_FRAME) == (block.getBlockType().getBigType() == eWasheBlockType.BIG_TYPE_FRAME)))) {
                canBoomBlockSet.add(block);
            }
        }
        return canBoomBlockSet;
    }

    private static void handleBombBoom(WasheConfig config, List<List<WasheBlock>> blockList, Set<WasheBlock> handleBlockSet, Set<WasheBlock> roundEliminateSet, List<WasheBoom> roundBoomList) {
        for (WasheBlock block : handleBlockSet) {
            List<eWasheBoomType> boomTypeList = getBoomType(block);
            for (eWasheBoomType boomType : boomTypeList) {

                // 炸弹效果
                WasheBoom boom = new WasheBoom(block.getX(), block.getY(), boomType, block.getDirection());
                boom.setBlockList(new ArrayList<>(getBoomArea(config, blockList, boom, roundEliminateSet)));

                // 添加爆炸效果
                roundBoomList.add(boom);
                // 添加消除格子
                roundEliminateSet.addAll(boom.getBlockList());
            }
        }
    }

    private static List<eWasheBoomType> getBoomType(WasheBlock block) {
        List<eWasheBoomType> boomTypeList = new ArrayList<>();

        eWasheBlockType blockType = block.getBlockType();
        if (blockType == eWasheBlockType.Hang) {
            boomTypeList.add(eWasheBoomType.Heng1);
        } else if (blockType == eWasheBlockType.Lie) {
            boomTypeList.add(eWasheBoomType.Shu1);
        } else if (blockType.getValue() == eWasheBlockType.DiLei.getValue() || blockType == eWasheBlockType.Landmine2) {
            boomTypeList.add(eWasheBoomType.LingXing);
        } else if (blockType == eWasheBlockType.LiuXing || blockType == eWasheBlockType.LiuXing_SkillCreate) {
            boomTypeList.add(eWasheBoomType.LiuXing);
        } else if (blockType == eWasheBlockType.GuiKeZhanBu) {
            boomTypeList.add(eWasheBoomType.GuiKeZhanBu);
        } else if (blockType == eWasheBlockType.Rocket || blockType == eWasheBlockType.YanHuoKuangHuan) {
            boomTypeList.add(eWasheBoomType.Rocket);
        } else if (blockType == eWasheBlockType.PaperFlower) {
            boomTypeList.add(eWasheBoomType.PaperFlowerSameColor);
        } else if (blockType == eWasheBlockType.DuNHuangFeiTian) {
            boomTypeList.add(eWasheBoomType.DunHuangFeiTian);
        }

        for (WasheBlockEffect effect : block.getEffectList()) {
            if (effect.getEffectType() == eWasheBlockEffectType.Skill_22) {
                if (effect.getEffectValue() == 1) boomTypeList.add(eWasheBoomType.DevilY);
                else if (effect.getEffectValue() == 2) boomTypeList.add(eWasheBoomType.DevilX);
                else if (effect.getEffectValue() == 3) boomTypeList.add(eWasheBoomType.DevilShiZi);
            }
        }

        return boomTypeList;
    }

    private static Set<WasheBlock> getBoomArea(WasheConfig config, List<List<WasheBlock>> blockList, WasheBoom boom, Set<WasheBlock> roundEliminateSet) {

        Set<WasheBlock> set = new HashSet<>();

        eWasheBoomType boomType = boom.getBoomType();
        int x = boom.getX();
        int y = boom.getY();

        if (boomType == eWasheBoomType.Heng1) {
            // 单行
            set.addAll(blockList.get(y));
        } else if (boomType == eWasheBoomType.Shu1) {
            // 单列
            for (int tmpY = 0; tmpY < blockList.size(); tmpY++) {
                set.add(blockList.get(tmpY).get(x));
            }
        } else if (boomType == eWasheBoomType.LingXing) {
            // 地雷
            if (isXyExist(blockList, x, y + 2)) set.add(blockList.get(y + 2).get(x));

            if (isXyExist(blockList, x - 1, y + 1)) set.add(blockList.get(y + 1).get(x - 1));
            if (isXyExist(blockList, x, y + 1)) set.add(blockList.get(y + 1).get(x));
            if (isXyExist(blockList, x + 1, y + 1)) set.add(blockList.get(y + 1).get(x + 1));

            if (isXyExist(blockList, x - 2, y)) set.add(blockList.get(y).get(x - 2));
            if (isXyExist(blockList, x - 1, y)) set.add(blockList.get(y).get(x - 1));
            if (isXyExist(blockList, x + 1, y)) set.add(blockList.get(y).get(x + 1));
            if (isXyExist(blockList, x + 2, y)) set.add(blockList.get(y).get(x + 2));

            if (isXyExist(blockList, x - 1, y - 1)) set.add(blockList.get(y - 1).get(x - 1));
            if (isXyExist(blockList, x, y - 1)) set.add(blockList.get(y - 1).get(x));
            if (isXyExist(blockList, x + 1, y - 1)) set.add(blockList.get(y - 1).get(x + 1));

            if (isXyExist(blockList, x, y - 2)) set.add(blockList.get(y - 2).get(x));
        } else if (boomType == eWasheBoomType.LiuXing) {
            // 流星
            List<WasheBlock> all = new ArrayList<>();
            for (List<WasheBlock> tmpList : blockList) {
                for (WasheBlock tmpBlock : tmpList) {
                    if (!roundEliminateSet.contains(tmpBlock)) {
                        all.add(tmpBlock);
                    }
                }
            }
            Collections.shuffle(all);
            int maxIndex = config.getLiuXingEliminateNum();
            for (int i = 0; i < maxIndex && i < all.size(); i++) {
                WasheBlock block = all.get(i);
                if (block.getX() == x && block.getY() == y) {
                    maxIndex++; // 跳过自己
                } else {
                    set.add(block);
                }
            }
        } else if (boomType == eWasheBoomType.GuiKeZhanBu) {
            // 龟壳占卜
            List<WasheBlock> all = new ArrayList<>();
            for (List<WasheBlock> tmpList : blockList) {
                for (WasheBlock tmpBlock : tmpList) {
                    if (tmpBlock.getBlockType() == eWasheBlockType.Normal && !roundEliminateSet.contains(tmpBlock)) {
                        all.add(tmpBlock);
                    }
                }
            }
            Collections.shuffle(all);
            int eliminateNum = WasheSceneLogic.scene_06_getEliminateNum(config);
            for (int i = 0; i < eliminateNum && i < all.size(); i++) {
                WasheBlock block = all.get(i);
                if (block.getX() == x && block.getY() == y) {
                    eliminateNum++; // 跳过自己
                } else {
                    set.add(block);
                }
            }
        } else if (boomType == eWasheBoomType.Rocket) {
            // 火箭
            if (boom.getDirection() == eWasheDirectionType.Up) {
                for (int tmpY = y + 1; tmpY < blockList.size(); tmpY++) {
                    set.add(blockList.get(tmpY).get(x));
                }
            } else if (boom.getDirection() == eWasheDirectionType.Down) {
                for (int tmpY = y - 1; tmpY >= 0; tmpY--) {
                    set.add(blockList.get(tmpY).get(x));
                }
            } else if (boom.getDirection() == eWasheDirectionType.Left) {
                for (int tmpX = 0; tmpX < x; tmpX++) {
                    set.add(blockList.get(y).get(tmpX));
                }
            } else if (boom.getDirection() == eWasheDirectionType.Right) {
                for (int tmpX = x + 1; tmpX < blockList.get(y).size(); tmpX++) {
                    set.add(blockList.get(y).get(tmpX));
                }
            }
        } else if (boomType == eWasheBoomType.PaperFlowerSameColor) {
            // 小窗花消除同色
            WasheBlock baseBlock = blockList.get(boom.getY()).get(boom.getX());
            for (List<WasheBlock> list : blockList) {
                for (WasheBlock block : list) {
                    if (block.getColor() == baseBlock.getColor()) {
                        set.add(block);
                    }
                }
            }
        } else if (boomType == eWasheBoomType.DevilY) {
            // 小恶魔Y
            for (int tmpY = 0; tmpY < boom.getY(); tmpY++) {
                set.add(blockList.get(tmpY).get(boom.getX()));
            }
            int addi = 0;
            while (true) {
                addi++;
                if (WasheGameLogic.isXyExist(blockList, boom.getX() + addi, boom.getY() + addi)) {
                    set.add(blockList.get(boom.getY() + addi).get(boom.getX() + addi));
                } else {
                    break;
                }
            }
            addi = 0;
            while (true) {
                addi++;
                if (WasheGameLogic.isXyExist(blockList, boom.getX() - addi, boom.getY() + addi)) {
                    set.add(blockList.get(boom.getY() + addi).get(boom.getX() - addi));
                } else {
                    break;
                }
            }
        } else if (boomType == eWasheBoomType.DevilX) {
            // 小恶魔X
            for (List<WasheBlock> list : blockList) {
                for (WasheBlock tmpBlock : list) {
                    int subY = tmpBlock.getY() - boom.getY();
                    int subX = tmpBlock.getX() - boom.getX();
                    if (subY == 0 || subX == 0) {
                        continue;
                    }
                    if (Math.abs(subX) == Math.abs(subY)) {
                        set.add(tmpBlock);
                    }
                }
            }
        } else if (boomType == eWasheBoomType.DevilShiZi) {
            // 小恶魔十字
            set.addAll(blockList.get(boom.getY()));
            for (int tmpY = 0; tmpY < blockList.size(); tmpY++) {
                WasheBlock tmpBlock = blockList.get(tmpY).get(boom.getX());
                set.add(tmpBlock);
            }
        } else if (boomType == eWasheBoomType.DunHuangFeiTian) {
            // 敦煌飞天
            for (int tmpY = y - 2; tmpY <= y + 2; tmpY++) {
                if (isXyExist(blockList, x, tmpY)) set.add(blockList.get(tmpY).get(x));
            }
            for (int tmpX = x - 2; tmpX <= x + 2; tmpX++) {
                if (isXyExist(blockList, tmpX, y)) set.add(blockList.get(y).get(tmpX));
            }
        }

        removeNoBaseFourBigType(set);
        return set;
    }

    private static void handleCombBombBoom(WasheConfig config, List<List<WasheBlock>> blockList, WasheMoveParam moveParam,
                                           Set<WasheBlock> roundEliminateSet, List<WasheBoom> roundBoomList) {
        if (moveParam == null) {
            return;
        }

        WasheBlock from = blockList.get(moveParam.getFromY()).get(moveParam.getFromX());
        WasheBlock to = blockList.get(moveParam.getToY()).get(moveParam.getToX());
        List<WasheBoom> boomList = new ArrayList<>();

        // 获取单独爆炸效果，其中可能包含effect的爆炸效果
        List<eWasheBoomType> fromBoomType = getBoomType(from);
        List<eWasheBoomType> toBoomType = getBoomType(to);

        if ((from.getBlockType() == eWasheBlockType.Hang || from.getBlockType() == eWasheBlockType.Lie) && (to.getBlockType() == eWasheBlockType.Hang || to.getBlockType() == eWasheBlockType.Lie)) {
            // 行/列+行/列 => 十字
            boomList.add(new WasheBoom(to.getX(), to.getY(), eWasheBoomType.Heng1, to.getDirection()));
            boomList.add(new WasheBoom(to.getX(), to.getY(), eWasheBoomType.Shu1, to.getDirection()));
            boomList.add(new WasheBoom(from.getX(), from.getY(), eWasheBoomType.Empty, to.getDirection()));
            fromBoomType.removeIf(item -> item == eWasheBoomType.Heng1 || item == eWasheBoomType.Shu1);
            toBoomType.removeIf(item -> item == eWasheBoomType.Heng1 || item == eWasheBoomType.Shu1);
            for (eWasheBoomType boomType : fromBoomType) {
                boomList.add(new WasheBoom(to.getX(), to.getY(), boomType, from.getDirection()));
            }
            for (eWasheBoomType boomType : toBoomType) {
                boomList.add(new WasheBoom(to.getX(), to.getY(), boomType, to.getDirection()));
            }

        } else if (from.getBlockType().getValue() == eWasheBlockType.DiLei.getValue() && to.getBlockType().getValue() == eWasheBlockType.DiLei.getValue()) {
            // 炸弹+炸弹
            for (eWasheBoomType boomType : fromBoomType) {
                boomList.add(new WasheBoom(from.getX(), from.getY(), boomType, from.getDirection()));
            }
            for (eWasheBoomType boomType : toBoomType) {
                boomList.add(new WasheBoom(to.getX(), to.getY(), boomType, to.getDirection()));
            }

        } else {
            // 其余的组合单独爆炸
            if (!isCombBombPart(from) || !isCombBombPart(to)) {
                return; // 不是组合
            }
            for (eWasheBoomType boomType : fromBoomType) {
                boomList.add(new WasheBoom(to.getX(), to.getY(), boomType, from.getDirection()));
            }
            for (eWasheBoomType boomType : toBoomType) {
                boomList.add(new WasheBoom(to.getX(), to.getY(), boomType, to.getDirection()));
            }
            boomList.add(new WasheBoom(from.getX(), from.getY(), eWasheBoomType.Empty, from.getDirection()));

        }

        // 添加消除的格子
        roundEliminateSet.add(from);
        roundEliminateSet.add(to);

        // 添加爆炸信息
        for (WasheBoom boom : boomList) {
            boom.setBlockList(new ArrayList<>(getBoomArea(config, blockList, boom, roundEliminateSet)));
            roundEliminateSet.addAll(boom.getBlockList());

            // 添加消除的格子
            roundBoomList.add(boom);
        }
    }

    private static void handleSameColorEliminate(List<List<WasheBlock>> blockList, Set<WasheBlock> roundEliminateSet) {
        for (List<WasheBlock> list : blockList) {
            for (WasheBlock block : list) {
                if (roundEliminateSet.contains(block)) {
                    continue;
                }
                if (!canSameColorEliminate(blockList, block)) {
                    continue;
                }
                roundEliminateSet.add(block);
            }
        }
    }

    private static void setEliminateEmpty(List<List<WasheBlock>> blockList, Set<WasheBlock> roundEliminateSet) {
        for (WasheBlock block : roundEliminateSet) {
            blockList.get(block.getY()).set(block.getX(), null);
        }
    }


    private static List<WasheBlock> createBomb(WasheConfig config, WasheMoveParam moveParam, WasheOperateResult result, Random random,
                                               WasheGameData gameData, Set<WasheBlock> lastRoundDropSet, int round,
                                               Set<WasheBlock> roundEliminateSet, WasheOperateRoundResult roundResult) {
        List<List<WasheBlock>> blockList = gameData.getBlockList();

        // 生成炸弹
        List<WasheBlock> logAddStepBlockList = new ArrayList<>();
        List<WasheBlock> newBombList = WasheCreateBombLogic.getCreateBombList(config, round, moveParam, roundEliminateSet, lastRoundDropSet, random, logAddStepBlockList, gameData);
        for (WasheBlock bomb : newBombList) {
            blockList.get(bomb.getY()).set(bomb.getX(), bomb);
        }
        roundResult.setNewBombList(newBombList);

        // 设置加步数格子
        if (logAddStepBlockList.size() > 0 && result.getBombAddStepStatus() == 0) {
            WasheOperateStageResult stage1 = roundResult.getStageResultList().get(0);
            stage1.getAddStepBlockList().addAll(logAddStepBlockList);
            result.setBombAddStepStatus(1);
        }
        return newBombList;
    }

    /**
     * @param gameData 只用来取下里面的计数数据，这里盘面是旧的
     */
    public static Set<WasheBlock> dropBlock(WasheConfig config, List<List<WasheBlock>> blockList, Random random, int guideDropNo, WasheGameData gameData, int roomRound, int turnPlayer) {

        Set<WasheBlock> dropSet = new HashSet<>();

        // 旧方块掉落
        for (int y = 0; y < blockList.size(); y++) {
            for (int x = 0; x < blockList.get(y).size(); x++) {
                if (blockList.get(y).get(x) == null) {
                    // 找他上面的格子掉落
                    for (int upY = y + 1; upY < blockList.size(); upY++) {
                        if (blockList.get(upY).get(x) != null) {
                            WasheBlock upBlock = blockList.get(upY).get(x).clone(); // 复制一个出来

                            upBlock.setY(y);
                            blockList.get(y).set(x, upBlock);
                            blockList.get(upY).set(x, null);

                            dropSet.add(upBlock);
                            break;
                        }
                    }
                }
            }
        }

        // 统计方块数量
        int emptyNum = 0;
        Map<Integer, Integer> colorCountMap = new HashMap<>();
        for (List<WasheBlock> list : blockList) {
            for (WasheBlock block : list) {
                if (block != null) {
                    int num = colorCountMap.getOrDefault(block.getColor(), 0);
                    colorCountMap.put(block.getColor(), num + 1);
                } else {
                    emptyNum++;
                }
            }
        }

        // 准备掉落方块
        List<Integer> dropColorList = new ArrayList<>();
        if (roomRound >= config.getDrop2OpenRound()) {
            // 【新掉落机制】
            List<Integer> drop2List = config.getDrop2List();
            int blueColor = turnPlayer == 1 ? eWasheColorType.Red.getValue() : eWasheColorType.Blue.getValue();
            for (int i = 0; i < emptyNum; i++) {
                List<Integer> weightList = new ArrayList<>();
                for (int color = 1; color <= config.getColorNum(); color++) {
                    int weight = colorCountMap.getOrDefault(color, 0) + drop2List.get(0);           // 当前数量+a
                    if (color == blueColor) {
                        weight += drop2List.get(1);                                                              // +b
                        weight *= drop2List.get(3);                                                              // *c2
                    } else {
                        weight *= drop2List.get(2);                                                              // *c1
                    }
                    weightList.add(weight);
                }
                int index = RandomHelper.getRandomIndexByWeight(weightList, random);
                int color = index + 1;
                dropColorList.add(color);
                colorCountMap.put(color, 1 + colorCountMap.getOrDefault(color, 0));
            }
        } else {
            // 【旧掉落机制】
            for (int i = 0; i < emptyNum; i++) {
                List<Integer> weightList = new ArrayList<>();
                for (int j = 0; j < config.getColorNum(); j++) {
                    weightList.add(config.getDropBaseWeight());
                }
                for (Map.Entry<Integer, Integer> entry : colorCountMap.entrySet()) {
                    if (entry.getValue() < config.getDropAddiCountLimit()) {
                        if (entry.getKey() == eWasheColorType.None.getValue()) {
                            continue;
                        }
                        int colorIndex = entry.getKey() - 1;
                        weightList.set(colorIndex, weightList.get(colorIndex) + config.getDropAddiWeight());
                    }
                }
                int index = RandomHelper.getRandomIndexByWeight(weightList, random);
                int color = index + 1;
                dropColorList.add(color);
                colorCountMap.put(color, 1 + colorCountMap.getOrDefault(color, 0));
            }
        }
        Collections.shuffle(dropColorList, random);

        // 新手引导替换下掉落方块
        if (guideDropNo > 0) {
            List<Integer> tmpList = WasheFirstGameLogic.getDropColorList(guideDropNo);
            for (int i = 0; i < dropColorList.size() && i < tmpList.size(); i++) {
                dropColorList.set(i, tmpList.get(i));
            }
        }

        // 掉落新方块
        int index = 0;
        List<WasheBlock> newDropList = new ArrayList<>();
        for (int y = 0; y < blockList.size(); y++) {
            for (int x = 0; x < blockList.get(y).size(); x++) {
                if (blockList.get(y).get(x) == null) {
                    int color = index < dropColorList.size() ? dropColorList.get(index) : random.nextInt(config.getColorNum()) + 1;
                    index++;

                    // 如果可以消除有概率要换个颜色
                    if (isXyExist(blockList, x - 1, y) && blockList.get(y).get(x - 1).getColor() == color &&
                            isXyExist(blockList, x - 2, y) && blockList.get(y).get(x - 2).getColor() == color ||
                            isXyExist(blockList, x + 1, y) && blockList.get(y).get(x + 1).getColor() == color &&
                                    isXyExist(blockList, x + 2, y) && blockList.get(y).get(x + 2).getColor() == color ||
                            isXyExist(blockList, x, y - 1) && blockList.get(y - 1).get(x).getColor() == color &&
                                    isXyExist(blockList, x, y - 2) && blockList.get(y - 2).get(x).getColor() == color) {
                        if (random.nextInt(1000) < config.getDropStableWeight()) {
                            int newColor = random.nextInt(config.getColorNum() - 1) + 1; // 少取一种颜色
                            if (newColor == color) {
                                newColor = config.getColorNum(); // 直接最后一种颜色
                            }
                            color = newColor;
                        }
                    }

                    WasheBlock block = new WasheBlock(x, y, color);
                    WasheSceneLogic.scene_12_checkEffectChange(block, gameData);
                    blockList.get(y).set(x, block);

                    dropSet.add(block);
                    newDropList.add(block);
                }
            }
        }

        // 盘面机制掉落替换
        WasheSceneLogic.replaceDropBlock(config, random, gameData, newDropList);

        return dropSet;
    }

    private static void checkRearrange(WasheOperateResult result, WasheGameData gameData, Random random) {

        WasheEliminateTip eliminateTips = getEliminateTip(gameData);
        if (eliminateTips != null) {
            return;
        }

        // 重排
        rearrangeBlock(gameData, random);

        // 添加重排结果
        WasheOperateRoundResult roundResult = new WasheOperateRoundResult();
        roundResult.setRearrange(true);
        roundResult.setGameData(gameData.clone());
        result.addRoundResult(roundResult);
    }

    public static boolean isCombBombPart(WasheBlock block) {
        if (block.getBlockType().getBigType() == eWasheBlockType.BIG_TYPE_NORMAL_BOMB) {
            return true;
        }
        return false;
    }

    // ======================= 技能 =======================

    private static void handleSkill_13_move(WasheConfig config, WasheGameData gameData,
                                            Set<WasheBlock> roundEliminateSet, List<WasheBoom> roundBoomList,
                                            WasheUserGameData userGameData, eWasheOperateType operateType) {
        WasheUserGameCard card = userGameData.getCard();
        if (card.getCardId() != eWasheSkillType.Card_13.getValue() || card.getTurnUseTimes() <= 0) {
            return;
        }
        if (operateType != eWasheOperateType.Move || roundEliminateSet.isEmpty()) {
            return;
        }

        for (List<WasheBlock> list : gameData.getBlockList()) {
            for (WasheBlock block : list) {
                if (!roundEliminateSet.contains(block) && block.getEffectList().stream().anyMatch(item -> item.getEffectType() == eWasheBlockEffectType.Skill_13)) {
                    roundEliminateSet.add(block);
                }
            }
        }
        fastBoom(config, gameData, roundEliminateSet, roundBoomList, operateType);
    }

    private static void handleSkill_13_moveEnd(WasheConfig config, WasheGameData gameData, WasheUserGameData userGameData, WasheOperateResult result, eWasheOperateType playerOperateType, Random random) {
        WasheUserGameCard card = userGameData.getCard();
        if (playerOperateType != eWasheOperateType.Move || card.getTurnUseTimes() <= 0) {
            return;
        }
        int addStep = result.getBombAddStepStatus() == 1 ? 1 : 0;
        for (WasheOperateShow show : result.getShowList()) {
            for (WasheOperateRoundResult roundResult : show.getRoundResultList()) {
                for (WasheOperateStageResult stageResult : roundResult.getStageResultList()) {
                    for (WasheBlock block : stageResult.getAddStepBlockList()) {
                        if (block.getBlockType() == eWasheBlockType.DianShanLeiMing) {
                            addStep += WasheSceneLogic.scene_09_getAddStepNum(config);
                        }
                    }
                }
            }
        }
        if (userGameData.getStep() <= 1 && addStep < 1) {
            return; // 本次移动结束后步数就为0 && 没有加步数
        }

        WasheSkillLogic.handleSkill_13(config, random, gameData, result, false);
    }

    private static void handleSkill_23_moveEnd(WasheConfig config, WasheGameData gameData, WasheUserGameData userGameData, WasheOperateResult result, eWasheOperateType playerOperateType, Random random) {
//        if (userGameData.getStep() <= 1 && result.getBombAddStepStatus() != 1) {
//            return; // 本次移动结束后步数就为0 && 没有加步数
//        }
        // 移动和使用卡片时才触发
        if (playerOperateType != eWasheOperateType.Move && playerOperateType != eWasheOperateType.UseCard) {
            return;
        }
        WasheCardConfig cardConfig = config.getCardConfigMap().get(eWasheSkillType.Card_23.getValue());
        // 只要存在机巧盒，就可以丢出炸弹
        WasheBlock smartBoxBlock = getSmartBoxBlock(gameData);
        if (smartBoxBlock == null) {
            return;
        }
        // 3.扔出炸弹
        // 获取可选方块
        List<WasheBlock> canRandomList = new ArrayList<>();
        for (List<WasheBlock> list : gameData.getBlockList()) {
            for (WasheBlock block : list) {
                if (block.getBlockType().getBigType() == eWasheBlockType.BIG_TYPE_NORMAL) {
                    canRandomList.add(block);
                }
            }
        }
        // 打乱
        Collections.shuffle(canRandomList, random);
        // 取方块变机巧盒炸弹
        List<WasheBlock> chooseList1 = new ArrayList<>();
        // 第一个格子是机巧盒
        chooseList1.add(smartBoxBlock);
        for (int i = 0; i < cardConfig.getSkillParamList().get(1) && i < canRandomList.size(); i++) {
            WasheBlock block = canRandomList.get(i);
            block.setBlockType(eWasheBlockType.Landmine2);
            chooseList1.add(block);
        }
        // 添加效果
        WasheOperateEffect effect3 = new WasheOperateEffect(eWasheSkillType.Card_23, 3);
        effect3.setBlockList(chooseList1);
        result.addEffect(effect3);
        // 技能结束
        result.addEffect(new WasheOperateEffect(eWasheSkillType.Card_23, 0));
    }

    public static WasheBlock getSmartBoxBlock(WasheGameData gameData) {
        for (List<WasheBlock> blockList : gameData.getBlockList()) {
            for (WasheBlock block : blockList) {
                if (block.getBlockType()== eWasheBlockType.SmartBox) {
                    return block;
                }
            }
        }
        return null;
    }


    private static int checkSkill_09_beforeUse(WasheGameData gameData, WasheUseSkillParam skillParam) {
        List<List<WasheBlock>> blockList = gameData.getBlockList();
        // 移动判断
        WasheMoveParam moveParam = new WasheMoveParam(skillParam.getX(), skillParam.getY(), skillParam.getToX(), skillParam.getToY());
        int ret = checkExchangeBlock(blockList, moveParam);
        if (ret != 0) {
            return ret;
        }
        // 能否同色消除
        exchangeBlock(blockList, moveParam.getFromX(), moveParam.getFromY(), moveParam.getToX(), moveParam.getToY(), null);
        WasheBlock from = blockList.get(moveParam.getFromY()).get(moveParam.getFromX());
        WasheBlock to = blockList.get(moveParam.getToY()).get(moveParam.getToX());
        if (!canSameColorEliminate(blockList, from) && !canSameColorEliminate(blockList, to)) {
            exchangeBlock(blockList, moveParam.getFromX(), moveParam.getFromY(), moveParam.getToX(), moveParam.getToY(), null);
            return GameErrorCode.E_WASHE_CAN_NOT_ELIMINATE;
        }
        exchangeBlock(blockList, moveParam.getFromX(), moveParam.getFromY(), moveParam.getToX(), moveParam.getToY(), null);
        return 0;
    }

    private static int checkSkill_11_beforeUse(WasheGameData gameData, WasheUseSkillParam skillParam) {
        List<List<WasheBlock>> blockList = gameData.getBlockList();
        if (!isXyExist(blockList, skillParam.getX(), skillParam.getY())) {
            return GameErrorCode.E_WASHE_POS_NO_EXIST;
        }
        WasheBlock block = blockList.get(skillParam.getY()).get(skillParam.getX());
        if (block.getColor() == 0) {
            return GameErrorCode.E_WASHE_BLOCK_NO_COLOR;
        }
        return 0;
    }

    private static int checkSkill_23_beforeUse(WasheGameData gameData, WasheUseSkillParam skillParam) {
        List<List<WasheBlock>> blockList = gameData.getBlockList();
        if (!isXyExist(blockList, skillParam.getX(), skillParam.getY())) {
            return GameErrorCode.E_WASHE_POS_NO_EXIST;
        }
        WasheBlock block = blockList.get(skillParam.getY()).get(skillParam.getX());
        if (block.getBlockType() == eWasheBlockType.SmartBox) {
            return GameErrorCode.E_WASHE_CAN_NOT_CHOOSE;
        }
        return 0;
    }

    private static int checkSkill_24_beforeUse(WasheGameData gameData, WasheUseSkillParam skillParam) {
        List<List<WasheBlock>> blockList = gameData.getBlockList();
        if (!isXyExist(blockList, skillParam.getX(), skillParam.getY())) {
            return GameErrorCode.E_WASHE_POS_NO_EXIST;
        }
        WasheBlock block = blockList.get(skillParam.getY()).get(skillParam.getX());
        if (block.getColor() == 0) {
            return GameErrorCode.E_WASHE_BLOCK_NO_COLOR;
        }
        return 0;
    }

    private static void handleSkill_22_eliminateEnd(WasheGameData gameData, WasheUserGameData userGameData, WasheOperateResult result, eWasheOperateType playerOperateType, Random random) {
        if (playerOperateType == eWasheOperateType.Move && userGameData.getStep() <= 1 && result.getBombAddStepStatus() != 1) {
            return; // 本次移动结束后步数就为0 && 没有加步数
        }

        // 找最后一次消除的数据
        List<WasheOperateRoundResult> roundResultList = new ArrayList<>();
        List<WasheOperateShow> showList = result.getShowList();
        for (int i = showList.size() - 1; i >= 0; i--) {
            WasheOperateShow operateShow = showList.get(i);
            if (operateShow.getRoundResultList().size() > 0) {
                roundResultList = operateShow.getRoundResultList();
                break;
            }
        }

        // 第一次使用掉的小恶魔方块
        List<WasheBlock> useList = new ArrayList<>();
        for (WasheOperateRoundResult roundResult : roundResultList) {
            for (WasheOperateStageResult stageResult : roundResult.getStageResultList()) {
                for (WasheBlock block : stageResult.getEliminateList()) {
                    boolean isSkill22Block = false;
                    for (WasheBlockEffect effect : block.getEffectList()) {
                        if (effect.getEffectType() == eWasheBlockEffectType.Skill_22 && effect.getTimes() < 1) {
                            isSkill22Block = true;
                            break;
                        }
                    }

                    if (isSkill22Block) {
                        useList.add(block);
                    }
                }
            }
        }
        if (useList.isEmpty()) {
            return;
        }

        // 找可以补位的位置
        List<WasheBlock> allList = new ArrayList<>();
        for (List<WasheBlock> list : gameData.getBlockList()) {
            for (WasheBlock block : list) {
                if (block.getBlockType().getBigType() == eWasheBlockType.BIG_TYPE_NORMAL) {
                    boolean isSkill22Block = false;
                    for (WasheBlockEffect effect : block.getEffectList()) {
                        if (effect.getEffectType() == eWasheBlockEffectType.Skill_22) {
                            isSkill22Block = true;
                            break;
                        }
                    }
                    if (!isSkill22Block) {
                        allList.add(block);
                    }
                }
            }
        }

        // 打乱
        Collections.shuffle(allList, random);

        // 随机飞过去
        WasheOperateEffect effect = new WasheOperateEffect(eWasheEffectType.DemonFly);
        for (int i = 0; i < useList.size() && i < allList.size(); i++) {

            WasheBlockEffect blockEffect = new WasheBlockEffect(eWasheBlockEffectType.Skill_22);
            blockEffect.setEffectValue(random.nextInt(3) + 1); // 1、2、3对应Y、X、十字
            blockEffect.setTimes(1);
            WasheBlock block = allList.get(i);
            block.addEffect(blockEffect);

            effect.addBlock(useList.get(i));
            effect.addBlock(block);
        }

        // 添加结果
        result.addEffect(effect);
    }

    private static int checkSkill_1002_beforeUse(WasheGameData gameData, WasheUseSkillParam skillParam) {
        List<List<WasheBlock>> blockList = gameData.getBlockList();
        if (!isXyExist(blockList, skillParam.getX(), skillParam.getY())) {
            return GameErrorCode.E_WASHE_POS_NO_EXIST;
        }
        int ret = WasheSceneLogic.scene_07_checkChooseBlock(skillParam, gameData, false);
        if (ret != 0) {
            return ret;
        }
        ret = WasheGameLogic.checkSkill_23_beforeUse(gameData, skillParam);
        if (ret != 0) {
            return ret;
        }
        return 0;
    }

    private static int checkSkill_1003_beforeUse(WasheGameData gameData, WasheUseSkillParam skillParam, WasheOperateResult result) {
        WasheMoveParam moveParam = new WasheMoveParam(skillParam.getX(), skillParam.getY(), skillParam.getToX(), skillParam.getToY());
        return WasheGameLogic.checkMoveBlock(gameData, moveParam, result);
    }

    private static int checkSkill_1011_beforeUse(WasheGameData gameData, WasheUseSkillParam skillParam) {
        List<List<WasheBlock>> blockList = gameData.getBlockList();
        if (!isXyExist(blockList, skillParam.getX(), skillParam.getY()) || !isXyExist(blockList, skillParam.getToX(), skillParam.getToY())) {
            return GameErrorCode.E_WASHE_POS_NO_EXIST;
        }
        if (skillParam.getX() == skillParam.getToX() && skillParam.getY() == skillParam.getToY()) {
            return GameErrorCode.E_WASHE_CAN_NOT_CHOOSE;
        }
        int ret = WasheSceneLogic.scene_07_checkChooseBlock(skillParam, gameData, true);
        if (ret != 0) {
            return ret;
        }
        ret = WasheGameLogic.checkSkill_23_beforeUse(gameData, skillParam);
        if (ret != 0) {
            return ret;
        }
        return 0;
    }

    public static List<List<WasheBlock>> getEmptyBlockList() {
        List<List<WasheBlock>> blockList = new ArrayList<>();
        for (int y = 0; y < 7; y++) {
            blockList.add(new ArrayList<>());
            for (int x = 0; x < 7; x++) {
                WasheBlock block = new WasheBlock();
                block.setX(x);
                block.setY(y);
                block.setColor(0);
                blockList.get(y).add(block);
            }
        }
        return blockList;
    }
}
